//==========================================所有权与引用============================================
// 所有权场景1：堆上元素移动
#[test]
fn test_move(){
    let s1 = String::from("hello");
    let s2 = s1;
    // 堆上元素赋值默认移动
    // 此时s1的所有权已经转移到了s2身上，所以此时操作s1是非法的
    println!("{}", s2)
}

// 所有权场景2：堆上元素拷贝
#[test]
fn test_clone(){
    let s1 = String::from("hello,clone.");
    let s2 = s1.clone();
    // 堆上元素也支持拷贝
    // 此时s1的拷贝赋值给了s2，所以s1 s2同时都是有效的
    println!("{} , {}", s1, s2)
}

// 场景2： 栈上元素拷贝
// eg. 栈上元素包括int float bool .. 和仅包含基本类型的元组(int, float)等
#[test]
fn test_copy(){
    let s1 = "123";
    let s2 = s1;
    // 栈上元素赋值默认支持拷贝
    // 此时s1 s2同时有效
    println!("{},{}", s1, s2)
}


// 所有权与函数调用
#[test]
fn test_ownership_func(){
    let s = String::from("hello func.");
    takes_ownership(s);
    let num = 10;
    make_copy(num)
}

fn takes_ownership(strs: String){
    println!("takes_ownership {}", strs);
    // strs为堆元素，此时会自动调用strs的drop()函数，释放掉这个String
}

fn make_copy(num: i32){
    println!("make_copy {}", num)
    // 但是num为栈上元素，此时不会自动调drop()释放，num只要处于作用域内就有效
}

//返回值与作用域
#[test]
fn test_return_ownership() {
    let s1 = give_ownership();

    /*
        let s2 = take_and_give_back(s);
        println!("{} {}", s1, s2)
        此刻会报错，因为s1中的所有权已经被转移到了s2中，所以不能再操作s1了
    */

    let s2 = String::from("hello");
    let s3 = take_and_give_back(s2);

    // 此时s2所有权转移到了s3上，所以s1 s3不冲突可以同时操作
    println!("{},{}", s1, s3)
}

fn give_ownership() -> String {
    let s = String::from("hello from ownership.");
    return s
}

fn take_and_give_back(str: String) -> String {
    return str
}

// 如果一个函数想对参数进行处理，但是又不想改变其所有权或者也不想在执行结束的时候自动释放掉该元素，怎么办？
#[test]
fn test_owner_return_process(){
    let s1 = String::from("hellos");

    //那么可以这样写,但是太形式主义了,这个问题可以用引用来解决
    let (s1, len) = take_and_process_return(s1);

    println!("{},{}", s1, len)
}

fn take_and_process_return(strs: String) -> (String, usize){
    let length = strs.len();
    return (strs, length)
}


// 引用与借用  （引用与借用是相对的关系，若B是A的引用则称B借用了A）
// 仅将元素借给函数使用，但是却不改变其所有者,也就是说在引用作用域内该元素不会被自动释放
#[test]
fn test_reference(){
    let mut s1 = String::from("hello");
    let length = refer_value(&s1);
    println!("{},{}", s1, length);
    let length = refer_mut_value(&mut s1);
    println!("{},{}", s1, length);
}

// 不可变引用
fn refer_value(s: &String) -> usize{
    // 这里是无法对s进行操作的，因为引用默认是不可变的，无法对其进行修改
    // s.push_str("123");
    return s.len()
}

// 可变引用
fn refer_mut_value(s: &mut String) -> usize {
    // 这里定义了一个可变引用,就能修改引用值了
    s.push_str("123");
    return s.len();
}


// 引用与数据竞争
#[test]
fn test_refer_multi() {
    let s = String::from("hello");

    // no problem
    let s1 = &s;
    // no problem
    let s2 = &s;

    // big problem, 因为s1 s2都是不可变的引用，他们不会互相干扰，所以代码正常运行
    //              但是s3确是可变的引用，那么一旦s3改变那么s1 s2势必受到影响
    //              所以元素不能同时出现多个包含可变引用的引用，只能出现多个不可变引用
    //let s3 = &mut s;

    println!("{}, {}", s1, s2)
}

// 避免悬垂引用
#[test]
fn test_refer_dangling(){
    let s = danglin();
    println!("{}", s)
}

fn danglin() -> String{
    let s = String::new();
    // 这里不能直接返回&s，因为一旦函数执行结束s就会被销毁，函数外就会操作一个已经被销毁的引用
    // 解决办法是转让所有权，直接返回一个移动元素
    return s
}
